package frame;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import libsvmCustom.svm_scale;
import models.BaseModel;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import protein.FastaInstanceReader;
import util.SystemUtil;
import feature.common.Extractor;
import feature.common.FeatureVector;
import feature.common.commonIntrface.ConfigBean;
import feature.common.commonIntrface.FeatureExtractor;
import feature.common.commonIntrface.FeatureFormatter;
import feature.common.commonIntrface.Instance;
import feature.common.commonIntrface.InstanceReader;
import feature.common.printer.PrinterProvider;

/**
 * @author Rishi Das Roy
 *
 * @Organization Institute Of Genomics & Integrative Biology
 */
public class FeatureHandler {

	private static Logger log = Logger.getLogger(FeatureHandler.class);

	private String mBasicFtrPath;

	private String[] mFilePaths;

	private String[] mlabels;

	private ApplicationContext mFtrFactory = new ClassPathXmlApplicationContext(
			"/protein/FeatureExtractor.xml");

	private ApplicationContext mFtrBeanFactory = new ClassPathXmlApplicationContext(
			"/protein/ConfigBeans.xml");

	private File mWrkSpc;

	private String mLabelPath;

	private List<Integer> mActualLabels = new ArrayList<Integer>();

	private List<String> mId = new ArrayList<String>();

	private int mPositiveCounts = 0;

	private int mNegativeCounts = 0;

	public FeatureHandler(String[] filePaths, String[] labels, File pWrkSpc)
			throws Exception {
		mWrkSpc = pWrkSpc;
		mFilePaths = filePaths;
		mlabels = labels;
		mBasicFtrPath = mWrkSpc.getAbsolutePath() + File.separator
				+ "AllFeature.libsvm";
		mLabelPath = mWrkSpc.getAbsolutePath() + File.separator + "label.txt";
		generateFeatures_Labels();

	}

	private void generateFeatures_Labels() throws FileNotFoundException,
			IOException, Exception {
		FeatureFormatter lFP = PrinterProvider.getPrinter("libsvm",
				mBasicFtrPath);
		String[] lBeanNames = mFtrBeanFactory.getBeanDefinitionNames();
		FeatureExtractor[] lFE = new FeatureExtractor[lBeanNames.length];
		for (int i = 0; i < lFE.length; i++) {
			lFE[i] = (FeatureExtractor) mFtrFactory.getBean(lBeanNames[i]);
			lFE[i].requirement((ConfigBean) mFtrBeanFactory
					.getBean(lBeanNames[i]));
		}
		Extractor lExt = new Extractor(lFE);
		InstanceReader pInstanceReader = new FastaInstanceReader();
		BufferedWriter lBW = new BufferedWriter(new FileWriter(mLabelPath));
		for (int i = 0; i < mFilePaths.length; i++) {
			pInstanceReader.setSource(mFilePaths[i]);
			// System.out.println("Processing "+mlabels[i]+" with label
			// "+mlabels[i]);
			executeFile(pInstanceReader, lExt, mlabels[i].trim(), lFP, lBW);
		}
		lBW.close();
	}

	public void executeFile(InstanceReader pInstanceReader, Extractor pExt,
			String pClassLabel, FeatureFormatter pFP, BufferedWriter pBw)
			throws Exception {
		Instance lInstance;
		int i = 0;
		int numOfFeature = 0;

		int label = Integer.parseInt(pClassLabel);
		try {
			while ((lInstance = pInstanceReader.nextInstance()) != null) {
				if(((String) lInstance.getInstanceData()).length() > 0){
					mActualLabels.add(label);
					mId.add(lInstance.getInstanceId().replaceAll(",", "__"));
					if (label >= 1) {
						mPositiveCounts++;
					} else if (label <= -1) {
						mNegativeCounts++;
					}
					FeatureVector lFV = new FeatureVector(
							lInstance.getInstanceId(), pClassLabel);
					pBw.write(pClassLabel + ">" + lInstance.getInstanceId()
							+ System.getProperty("line.separator"));
					try {
						lFV.addAll(pExt.extract(lInstance.getInstanceData()));
					} catch (RuntimeException e) {
						// TODO implement wisely
						System.err.println("error at >"+lInstance.getInstanceId());
						System.err.println(""+lInstance.getInstanceData());
						log.error(e);
						throw e;
					}
					if (numOfFeature == 0) {
						numOfFeature = lFV.size();
					} else if (numOfFeature != lFV.size()) {
						System.err.println(i + " " + lInstance.getInstanceId());
						System.exit(0);
					}
					pFP.printVector(lFV);
				}

			}
			if (pFP.isHasError()) {
				// System.out.println("Error found in generated attributes for
				// Instances of"+pInstanceReader.getSource());
				// System.out.println("see log file");
				log
						.error("Error found in generated attributes for Instances of"
								+ pInstanceReader.getSource());
				for (String id : pFP.getErrorInstanceID()) {
					log.error(id);
				}
			}
		} catch (IOException e) {
			log.error("Error in input resource, after processing " + i
					+ " instances" + e);
			throw e;
		} catch (RuntimeException e) {
			log.error("Error in input resource, after processing " + i
					+ " instances" + e);
			throw e;
		}

	}

	public static boolean validateFastaFile(String[] pInputFilePaths)
			throws FileNotFoundException, IOException, Exception {
		InstanceReader pInstanceReader = new FastaInstanceReader();

		for (int i = 0; i < pInputFilePaths.length; i++) {
			pInstanceReader.setSource(pInputFilePaths[i]);

			BufferedWriter lBW = new BufferedWriter(new FileWriter(pInputFilePaths[i]+".cured.faa"));
			Instance lInstance;
			while ((lInstance =pInstanceReader.nextInstance()) != null) {
				lBW.write(">"+lInstance.getInstanceId()+"\n");
				lBW.write(((String)lInstance.getInstanceData())+"\n");
			}
			lBW.close();
			if (pInstanceReader.hasError()) {
				System.err.println("There are error in input files");
				System.err.println(pInstanceReader.getError());
				log.error(pInstanceReader.getError());
				String lErrMsg = pInstanceReader.getError().substring(
						pInstanceReader.getError().indexOf("\n", 1) + 1);
				throw new Exception("Problem in following proteins\n" + lErrMsg);
			}else {
				SystemUtil.delete(pInputFilePaths[i]+".cured.faa");
			}
		}

		return !pInstanceReader.hasError();
	}

	public String extactScaledFeatures(BaseModel model, List<String> ftrList)
			throws IOException {
		BufferedReader baseFtrs = new BufferedReader(new FileReader(
				mBasicFtrPath));
		File tmpFile = extractTempFeatures(baseFtrs, model, ftrList);
		return scaleFeatures(model, tmpFile);
	}

	private String scaleFeatures(BaseModel model, File tmpFile)
			throws IOException {
		String scaledFilePath = mWrkSpc.getAbsolutePath() + File.separator
				+ model.getName();
		File scaledFile = new File(scaledFilePath);
		if (!scaledFile.exists()) {
			scaledFile.mkdirs();
		}
		scaledFilePath = mWrkSpc.getAbsolutePath() + File.separator
				+ model.getName() + File.separator + model.getName()
				+ ".libsvm";
		String[] argv = { "-r", SystemUtil.getClassPath(model.getScalePath()),
				tmpFile.getAbsolutePath(), ">", scaledFilePath };
		svm_scale.main(argv);
		return scaledFilePath;
	}

	private File extractTempFeatures(BufferedReader baseFtrs, BaseModel model,
			List<String> ftrList) throws IOException {
		// File tmpFile = new File( SystemUtil.getNewTmpFile());
		File tmpFile = new File(mWrkSpc.getAbsolutePath() + File.separator
				+ model.getName() + "_NonScale.libsvm");
		BufferedWriter tmpFileWrt = new BufferedWriter(new FileWriter(tmpFile));
		String line;
		while ((line = baseFtrs.readLine()) != null) {
			String[] features = line.split("\\s");
			tmpFileWrt.write(features[0]);
			for (int i = 1; i < features.length; i++) {
				String id = features[i].split(":")[0];
				if (ftrList.contains(id)) {
					tmpFileWrt.write(" " + features[i]);
				}
			}
			tmpFileWrt.write(System.getProperty("line.separator"));
		}
		tmpFileWrt.close();
		return tmpFile;
	}

	public String getLabelPath() {
		return mLabelPath;
	}

	public List<Integer> getActualLabels() {
		return mActualLabels;
	}

	public int getNegativeCounts() {
		return mNegativeCounts;
	}

	public int getPositiveCounts() {
		return mPositiveCounts;
	}

	public List<String> getId() {
		return mId;
	}

}
